﻿using System;
using System.Collections.ObjectModel;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Interop;
using System.Windows.Media;
using FFmpegVideoClip.Configuration;
using FFmpegVideoClip.Lib;
using FFmpegVideoClip.Lib.AudioSpectrogram;
using FFmpegVideoClip.Lib.ClipGroups;
using FFmpegVideoClip.Lib.Controls;
using FFmpegVideoClip.Lib.TaskFlowProcessor;
using FFmpegVideoClip.Lib.UtilityControlsTools;
using FFmpegVideoClip.Mode;
using FFmpegVideoClip.Processing;
using FFmpegVideoClip.windows;
using ICSharpCode.AvalonEdit.Highlighting;
using Microsoft.Win32;
using Microsoft.WindowsAPICodePack.Dialogs;
using Unosquare.FFME;
using UtilityToolsCollect.ObjectsLibrary.ProcessManage;
using UtilityToolsCollect.ObjectsLibrary.TaskFlowProcessor;
using static FFmpegVideoClip.Lib.GetMediaInfo;


namespace FFmpegVideoClip
{
    /// <summary>
    /// Interaction logic for MainWindow.xaml
    /// </summary>
    public partial class MainWindow : Window
    {

        public static MainWindow? Instance { get; private set; }
        private static System.Drawing.Size _desktopSize;
        public static System.Drawing.Size desktopSize => _desktopSize;
        public static void ShowStatusProgress() { if (Instance == null) return; Instance.StatusInfo.Visibility = Visibility.Collapsed; Instance.StatusProgress.Visibility = Visibility.Visible; Instance.RootGrid.IsEnabled = false; }
        public static void CloseStatusProgress() { if (Instance == null) return; Instance.StatusProgress.Visibility = Visibility.Collapsed; Instance.RootGrid.IsEnabled = true; }
        public static void ShowStatusInfo(string Text, Brush? InfoBackground = null) { if (Instance == null) return; Instance.StatusInfo.Visibility = Visibility.Visible; if (InfoBackground != null) { Instance.StatusInfo.Background = InfoBackground; } Instance.StatusInfo.Text = Text; }
        public static void CloseStatusInfo() { if (Instance == null) return; Instance.StatusInfo.Visibility = Visibility.Collapsed; }

        public TextBox? CurrentEditTextbox;

        public MediaElementConcrol Mec { get => (MediaElementConcrol)GetValue(mmecProperty); private set => SetValue(mmecProperty, value); }
        public static readonly DependencyProperty mmecProperty = DependencyProperty.Register(nameof(Mec), typeof(MediaElementConcrol), typeof(MainWindow), new PropertyMetadata(null));

        TextBoxTextTimeInputChangedLimit BoxTextTimeInputLimit;
        private bool IsSetFocus = false;
        private MenuItem AddToGruop = new();
        public MainWindow()
        {
            Instance = this;
            HighlightingManager.Instance.RegisterHighlighting("MediaToolsConfig", new string[] { "*.*" }, new ConfigHighlightingDefinition());
            Library.FFmpegDirectory = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Tools", "FFmpeg");

            InitializeComponent();
            AddToGruop.Header = "添加到组";
            Mec = new(this, MediaPlayer, this.JdSlider, this.MmSlider, this.HmSlider, this.TimeLine, this.PlayButton, this.StopButton);

            StatusInfo.Visibility = Visibility.Collapsed;
            StatusInfo.Text = string.Empty;

            this.AllowDrop = true;
            this.DragEnter += DropOpenMediaEnter;
            this.Drop += DropOpenMedia;
            //System.Diagnostics.Debug.WriteLine(Configuration.Config.CacheDirectory);
            BoxTextTimeInputLimit = new();
            BoxTextTimeInputLimit.RegisterControl(StartTimeText);
            BoxTextTimeInputLimit.RegisterControl(EndTimeText);
            Loaded += MainWindow_Loaded;
            Closed += MainWindow_Closed;
        }

        private void MainWindow_Loaded(object sender, RoutedEventArgs e)
        {
            _desktopSize = System.Windows.Forms.Screen.FromHandle(new WindowInteropHelper(this).Handle).WorkingArea.Size;
            SystemEvents.DisplaySettingsChanged += SystemEvents_DisplaySettingsChanged;
        }

        private void SystemEvents_DisplaySettingsChanged(object? sender, EventArgs e)
        {
            _desktopSize = System.Windows.Forms.Screen.FromHandle(new WindowInteropHelper(this).Handle).WorkingArea.Size;
        }

        private void MainWindow_Closed(object? sender, EventArgs e) { SystemEvents.DisplaySettingsChanged -= SystemEvents_DisplaySettingsChanged; Instance = null; }

        /// <summary>
        /// 拖拽文件媒体文件-进入到窗口
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void DropOpenMediaEnter(object sender, DragEventArgs e)
        {
            if (e.Data.GetDataPresent(DataFormats.FileDrop))
                e.Effects = DragDropEffects.Link;
            else
                e.Effects = DragDropEffects.None;
        }
        /// <summary>
        /// 拖拽文件媒体文件-打开
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void DropOpenMedia(object sender, DragEventArgs e)
        {
            if (!e.Data.GetDataPresent(DataFormats.FileDrop))
                return;

            string FilePath = ((string[])e.Data.GetData(DataFormats.FileDrop)).FirstOrDefault(string.Empty);
            if (string.IsNullOrEmpty(FilePath))
                return;
#pragma warning disable CS4014 // 由于此调用不会等待，因此在调用完成前将继续执行当前方法
            Mec.OpenMedia(FilePath, () => SetCurrentMediaInfo_Click(null!, null!));
#pragma warning restore CS4014 // 由于此调用不会等待，因此在调用完成前将继续执行当前方法
        }


        AdornerLayer? adornerLayer;
        FragmentDragDropAdorner? adorner;

        /// <summary>
        /// 剪辑拖拽事件-鼠标移动
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void ListBox_PreviewMouseMove(object sender, MouseEventArgs e)
        {
            ListBox? listBox = sender as ListBox;

            if (listBox != null && e.LeftButton == MouseButtonState.Pressed)
            {
                TextBlock textBlock;
                try
                {
                    textBlock = (TextBlock)e.OriginalSource;
                    adorner = new FragmentDragDropAdorner(textBlock);
                    adornerLayer = AdornerLayer.GetAdornerLayer(RootGrid);
                    adornerLayer.Add(adorner);
                    DragDrop.DoDragDrop(listBox, new DataObject(textBlock.DataContext), DragDropEffects.Copy);
                    adornerLayer?.Remove(adorner);
                    adornerLayer = null;
                    adorner = null;
                }
                catch (Exception)
                {
                    return;
                }
            }
        }

        /// <summary>
        /// 剪辑拖拽事件-拖拽完成
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void ListBox_PreviewDrop(object sender, DragEventArgs e)
        {
            Fragment[]? DragSource = null;
            Fragment? currentFrament = null;
            Clip? PickClip = (sender as ListBox)?.DataContext as Clip;
            FrameworkElement? PickItem = e.OriginalSource as FrameworkElement;
            Fragment? PickSource = PickItem?.DataContext as Fragment;

            //检查鼠标指向的剪辑及目标，两个要素任意一个为Null，则代表该次操作鼠标指向的目标有误
            if (PickClip is null || PickItem is null)
                return;

            //获取文件拖拽，如果成功，代表本次操作是一个文件拖拽动作，直接创建Fragment数据给DragSource赋值
            if (e.Data.GetDataPresent(DataFormats.FileDrop))
            {
                string[] FilePaths;
                try { FilePaths = (string[])e.Data.GetData(DataFormats.FileDrop); }
                catch { LogManage.WriteLineNewInfo("文件拖拽失败 文件路径过长"); MessageBoxEx.Show(this, "文件拖拽失败：文件路径过长"); return; }
                var Info = FilePaths.Where(p => !string.IsNullOrEmpty(p) && File.Exists(p)).Select(p => (path: p, Info: GetMediaInfo.GetInfo(p))).Where(p => p.Info.IsValidFile).ToArray();
                if (Info.Length < 1) { e.Handled = true; return; }
                DragSource = new Fragment[Info.Length];
                (string path, MediaInfoResult Info) currentFile;
                for (int i = 0; i < Info.Length; i++)
                {
                    currentFile = Info[i];
                    DragSource[i] = new()
                    {
                        EndTimeValue = Tools.GetTimeSpanToString(currentFile.Info.Duration),
                        Name = Path.GetFileNameWithoutExtension(currentFile.path),
                        TempWritePath = currentFile.path,
                        InputPath = currentFile.path,
                        ID = ClipManage.NewFragmentID(),
                        IsVideoStream = currentFile.Info.IsVideoStream,
                        IsAudioStream = currentFile.Info.IsAudioStream,
                        IsSubStream = currentFile.Info.IsSubStream
                    };
                }
            }
            //如果上一步没有成功，则代表本次操作是一次内部数据的拖拽操作，直接获取被拖拽的Fragment赋值给DragSource
            if (DragSource == null || DragSource.Length < 1)
            {
                //如果不是copy操作，代表本次操作有误
                if (e.Effects != DragDropEffects.Copy)
                    return;
                currentFrament = e.Data.GetData(typeof(Fragment)) as Fragment;
                if (currentFrament is null)
                    return;
                DragSource = new[] { currentFrament };
            }
            //检查当前的片段[Fragment]是否与目标剪辑[Clip]中的片段有重名
            if (!Tools.CheckFragmentName(PickClip.Fragments, DragSource, true))
            {
                MessageBoxEx.Show(this, "无法解决重命名，请手动核对片段名称");
                e.Handled = true;
                return;
            }
            //检查鼠标拖拽最终指向的目标是否是一个Fragment，
            //如果指向的目标不是Fragment 则表示，拖拽指向的剪辑中没有片段数据
            //因此不用考虑排序，直接将拖拽数据(如果存在原剪辑)从原剪辑移除，添加到新的剪辑中
            if (PickSource is null)
            {
                for (int i = 0; i < DragSource.Length; i++)
                {
                    currentFrament = DragSource[i];
                    currentFrament.clip?.Remove(currentFrament);
                    PickClip.AddFragment(currentFrament);
                    if (string.IsNullOrWhiteSpace(PickClip.Name))
                    {
                        PickClip.Name = currentFrament.Name;
                        ClipManage.DuplicateNameCheck();
                    }
                }
                e.Handled = true;
                return;
            }
            //上一步中如果鼠标确实指向了一个Fragment片段，则获取并计算鼠标指向的是片段上的左侧还是右侧
            //从而确定将拖拽数据放在指向的目标Fragment片段的前面还是后面
            double MouseX = e.GetPosition(PickItem).X;
            double CenterX = PickItem.ActualWidth / 2;
            bool IsRight = MouseX > CenterX;
            for (int i = 0; i < DragSource.Length; i++)
            {
                currentFrament = DragSource[i];
                currentFrament.clip?.Remove(currentFrament);

                int PickIndex = PickClip.IndexOf(PickSource);
                if (IsRight)
                    ++PickIndex;

                PickClip.Insert(PickIndex, currentFrament);
                if (IsRight) PickSource = currentFrament;
            }
            e.Handled = true;
        }
        /// <summary>
        /// 剪辑拖拽事件-当拖拽-顶级Grid元素-全窗体事件
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void RootGrid_PreviewDragOver(object sender, DragEventArgs e)
        {
            if (adornerLayer != null)
            {
                adornerLayer.Update();
            }
        }
        /// <summary>
        /// 文件拖入剪辑队列
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void ClipsListBox_Drop(object sender, DragEventArgs e)
        {
            if (!e.Data.GetDataPresent(DataFormats.FileDrop))
            {
                Tools.ObservableCollection_Drop<ListBoxItem, Clip>(sender, e, ClipManage.ClipList, DragDropEffects.Copy, false);
                return;
            }

            Clip? PickClip = (sender as ListBox)?.DataContext as Clip;
            //检查鼠标是否指向一个剪辑
            if (PickClip is not null)
                return;

            string SavePath = string.Empty;
            string[] paths;
            try { paths = (string[])e.Data.GetData(DataFormats.FileDrop); }
            catch { LogManage.WriteLineNewInfo("文件拖拽失败 文件路径过长"); MessageBoxEx.Show(this, "文件拖拽失败：文件路径过长"); return; }
            var Fileinfos = paths.Where(p => !string.IsNullOrEmpty(p) && File.Exists(p)).Select(p => (Path: p, Info: p.EndsWith("cue", StringComparison.OrdinalIgnoreCase) ? new MediaInfoResult(TimeSpan.Zero, ForceValidFile: true) : GetMediaInfo.GetInfo(p))).ToArray();
            Fileinfos = Fileinfos.Where(p => p.Info.IsCompriseForceValidFile).ToArray();
            if (Fileinfos.Length < 1)
                return;

            if (Fileinfos.Length < Config.DragInFileBoundsCount && ClipManage.ClipCount > 0)
                SavePath = ClipManage.ClipList.Where(p => !string.IsNullOrEmpty(p.OutDirectory)).LastOrDefault()?.OutDirectory ?? string.Empty;

            if (Fileinfos.Length == 1 && Path.GetExtension(Fileinfos.First().Path).EndsWith("cue", StringComparison.OrdinalIgnoreCase))
            {
                CUESheets.LoadFileInToClips(this, Fileinfos.First().Path);
                e.Handled = true;
                return;
            }

            if (string.IsNullOrEmpty(SavePath))
            {
                using (CommonOpenFileDialog dialog = new CommonOpenFileDialog())
                {
                    dialog.Title = "选择保存路径";
                    dialog.IsFolderPicker = true;
                    dialog.InitialDirectory = !string.IsNullOrEmpty(ClipManage.CurrentEditFragment?.clip?.OutDirectory) ? ClipManage.CurrentEditFragment.clip.OutDirectory :
                    !string.IsNullOrEmpty(MediaElementConcrol.LastPath) ? MediaElementConcrol.LastPath : Environment.GetFolderPath(Environment.SpecialFolder.MyComputer);
                    dialog.EnsurePathExists = true;
                    if (dialog.ShowDialog() == CommonFileDialogResult.Cancel)
                        return;
                    SavePath = dialog.FileName;
                }
            }

            Clip current;
            int ID = ClipManage.NewFragmentID();
            string endTime;
            foreach (var item in Fileinfos.Where(p => p.Info.IsValidFile))
            {
                try { endTime = Tools.GetTimeSpanToString(item.Info.Duration); }
                catch
                {
                    LogManage.WriteLineNewInfo($"无法获取媒体文件:[{item.Path}]的时间长度，载入失败");
                    continue;
                }

                current = new Clip { Name = Path.GetFileNameWithoutExtension(item.Path), OutDirectory = SavePath };

                current.AddFragment(new()
                {
                    Name = "Fragment",
                    clip = current,
                    ID = ID++,
                    InputPath = item.Path,
                    StartTimeValue = TextBoxTextTimeInputChangedLimit.DefaultHistorical,
                    EndTimeValue = endTime,
                    IsVideoStream = item.Info.IsVideoStream,
                    IsAudioStream = item.Info.IsAudioStream,
                    IsSubStream = item.Info.IsSubStream
                });

                ClipManage.ClipList.Add(current);
            }
            ClipManage.DuplicateNameCheck();
            e.Handled = true;
        }

        /// <summary>
        /// 启用标题编辑-双击剪辑标题
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void TextBox_PreviewMouseDoubleClick(object sender, MouseButtonEventArgs e)
        {
            TextBox textBox = (TextBox)sender;
            if (textBox.IsReadOnly)
            {
                textBox.IsReadOnly = false;
                CurrentEditTextbox = textBox;
            }
        }

        /// <summary>
        /// 结束标题编辑-焦点离开
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void TextBox_LostFocus(object sender, RoutedEventArgs? e)
        {
            if (IsSetFocus)
                return;
            TextBox textBox = (TextBox)sender;
            ((Clip)textBox.DataContext).Name = textBox.Text;
            if (ClipManage.ClipList.CheckDuplicate(p => p.Name, StringComparer.OrdinalIgnoreCase))
            {
                MessageBoxEx.Show(this, "命名重名", "命名重名错误", MessageBoxButton.OK, MessageBoxImage.Error);
                Task.Run(() =>
                {
                    Dispatcher.BeginInvoke(() =>
                    {
                        Thread.Sleep(500);
                        IsSetFocus = true;
                        CurrentEditTextbox?.Focus();
                        Keyboard.Focus(CurrentEditTextbox);
                        IsSetFocus = false;
                    });
                });

                return;
            }
            if (!textBox.IsReadOnly)
            {
                textBox.IsReadOnly = true;
                CurrentEditTextbox = null;
            }
        }
        private void TextBox_OutDirectory_LostFocus(object sender, RoutedEventArgs? e)
        {
            TextBox textBox = (TextBox)sender;
            ((Clip)textBox.DataContext).OutDirectory = textBox.Text;
            if (!textBox.IsReadOnly)
            {
                textBox.IsReadOnly = true;
                CurrentEditTextbox = null;
            }
        }
        /// <summary>
        /// 结束标题编辑-回车键
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void TextBox_PreviewKeyDown(object sender, KeyEventArgs e)
        {
            if (e.Key == Key.Enter)
            {
                TextBox_LostFocus(sender, null);
            }
        }
        private void TextBox_OutDirectory_PreviewKeyDown(object sender, KeyEventArgs e)
        {
            if (e.Key == Key.Enter)
            {
                TextBox_OutDirectory_LostFocus(sender, null);
            }
        }
        /// <summary>
        /// 选择剪辑中的片段-当选择变化
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void ListBox_SelectionChanged(object sender, SelectionChangedEventArgs e)
        {
            if (e.AddedItems.Count < 1)
                return;
            if (ClipManage.CurrentEditFragment is null)
                return;
            ClipManage.CurrentEditFragment.CopyToSet((Fragment)e.AddedItems[0]!);
            ClipList_ListBox.SelectedItem = ClipManage.CurrentEditFragment.clip;
        }
        /// <summary>
        /// 选择剪辑中的片段-取消选择-当失去焦点
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void ListBox_LostFocus(object sender, RoutedEventArgs e)
        {
            ((ListBox)sender).UnselectAll();
        }

        /// <summary>
        /// 删除剪辑
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void ClipRow_DeleteClipButton_Click(object sender, RoutedEventArgs e)
        {
            if (MessageBoxEx.Show(this, "确认删除剪辑，并同时删除剪辑中的内容么?", "删除剪辑确认", MessageBoxButton.OKCancel, MessageBoxImage.Warning, MessageBoxResult.Cancel) == MessageBoxResult.Cancel)
                return;
            Clip clip = (Clip)((FrameworkElement)e.OriginalSource).DataContext;
            ClipsGroupManage.GroupDelet(clip);
            ClipManage.ClipList.Remove(clip);
        }

        /// <summary>
        /// 新建剪辑
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void CreateNewClipButtonClick(object sender, RoutedEventArgs e) => ClipManage.CreateNewClip(out Clip clip);

        /// <summary>
        /// 设置剪辑保存路径
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void SetClipOutPathButton_Click(object sender, RoutedEventArgs e)
        {
            Clip clip = (Clip)((Button)sender).DataContext;

            using (CommonOpenFileDialog dialog = new CommonOpenFileDialog())
            {
                dialog.Title = "选择保存路径";
                dialog.IsFolderPicker = true;
                dialog.InitialDirectory = !string.IsNullOrEmpty(clip.OutDirectory) ? clip.OutDirectory :
                !string.IsNullOrEmpty(ClipManage.CurrentEditFragment?.clip?.OutDirectory) ? ClipManage.CurrentEditFragment.clip.OutDirectory :
                !string.IsNullOrEmpty(MediaElementConcrol.LastPath) ? MediaElementConcrol.LastPath : Environment.GetFolderPath(Environment.SpecialFolder.MyComputer);
                dialog.EnsurePathExists = true;
                if (dialog.ShowDialog() == CommonFileDialogResult.Cancel)
                    return;
                clip.OutDirectory = dialog.FileName;
            }
        }

        /// <summary>
        /// 取时间
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void GetTimeButton_Click(object sender, RoutedEventArgs e)
        {
            TimeWindowShow.ShowTimeDialog(this, "设置剪辑", 200);
            if (!Mec.CurrentMediaPath.Equals(ClipManage.CurrentEditFragment.InputPath, StringComparison.OrdinalIgnoreCase))
            {
                ClipManage.CurrentEditFragment.InputPath = Mec.CurrentMediaPath;
            }
            if (((Button)sender).Tag.ToString()!.Equals("Start", StringComparison.OrdinalIgnoreCase))
                ClipManage.CurrentEditFragment.StartTimeValue = Tools.GetTimeSpanToString(Mec.CurrentTimePosition);
            else
                ClipManage.CurrentEditFragment.EndTimeValue = Tools.GetTimeSpanToString(Mec.CurrentTimePosition);
        }

        private void CommandBinding_CanExecute(object sender, CanExecuteRoutedEventArgs e)
        {
            if (e.Parameter.ToString()!.Equals("Start", StringComparison.Ordinal) && GetStartTime.IsEnabled)
                e.CanExecute = true;
            else if (GetEndTime.IsEnabled)
                e.CanExecute = true;
            else e.CanExecute = false;
        }

        private void CommandBinding_Executed(object sender, ExecutedRoutedEventArgs e)
        {
            TimeWindowShow.ShowTimeDialog(this, "设置剪辑", 200);
            if (e.Parameter.ToString()!.Equals("Start", StringComparison.Ordinal))
                ClipManage.CurrentEditFragment.StartTimeValue = Tools.GetTimeSpanToString(Mec.CurrentTimePosition);
            else
                ClipManage.CurrentEditFragment.EndTimeValue = Tools.GetTimeSpanToString(Mec.CurrentTimePosition);
        }


        /// <summary>
        /// 时间定位事件
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void TimeLocationButton_Click(object sender, RoutedEventArgs e)
        {
            Mec.CurrentTimePosition = Tools.GetStringTimeToTimeSpan(((Button)sender).Tag.ToString()!);
        }

        /// <summary>
        /// 播放当前片段
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private async void PlayCurrentEditFragmentButton_Click(object sender, RoutedEventArgs e)
        {
            if (!Mec.CurrentMediaPath.Equals(ClipManage.CurrentEditFragment.InputPath, StringComparison.OrdinalIgnoreCase))
                await Mec.OpenMedia(ClipManage.CurrentEditFragment.InputPath);
            Mec.Pause();
            Mec.CurrentTimePosition = Tools.GetStringTimeToTimeSpan(ClipManage.CurrentEditFragment.StartTimeValue);
        }

        /// <summary>
        /// 打开当前片段
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void OpenCurrentEditFragmentButton_Click(object sender, RoutedEventArgs e)
        {
            if (Mec.CurrentMediaPath.Equals(ClipManage.CurrentEditFragment.InputPath, StringComparison.OrdinalIgnoreCase))
                return;
#pragma warning disable CS4014 // 由于此调用不会等待，因此在调用完成前将继续执行当前方法
            Mec.OpenMedia(ClipManage.CurrentEditFragment.InputPath);
#pragma warning restore CS4014 // 由于此调用不会等待，因此在调用完成前将继续执行当前方法
        }

        /// <summary>
        /// 添加当前片段到剪辑
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void AddNewFragment_Click(object sender, RoutedEventArgs e)
        {
            if (ClipManage.CurrentEditFragment.clip is null)
            {
                MessageBoxEx.Show(this, "片段没有指定剪辑，请选择剪辑。", "操作错误", MessageBoxButton.OK, MessageBoxImage.Error);
                return;
            }

            if (!Tools.CheckStartAndEndTime())
            {
                MessageBoxEx.Show(this, "结束时间小于开始时间", "时间范围错误", MessageBoxButton.OK, MessageBoxImage.Error);
                return;
            }

            Fragment fragment = new Fragment();
            ClipManage.CurrentEditFragment.CopyTo(fragment);
            fragment.ID = ClipManage.NewFragmentID();

            if (!Tools.CheckFragmentName(this, ClipManage.CurrentEditFragment.clip!, fragment))
                return;

            fragment.clip!.AddFragment(fragment);
            if (string.IsNullOrWhiteSpace(fragment.clip.Name))
            {
                fragment.clip.Name = fragment.Name;
                ClipManage.DuplicateNameCheck();
            }
            if (string.IsNullOrEmpty(fragment.InputPath))
                fragment.InputPath = Mec.CurrentMediaPath;
            ClipManage.CurrentEditFragment.CopyToSet(fragment);
        }

        /// <summary>
        /// 保存片段设置
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void SaveFragment_Click(object sender, RoutedEventArgs e)
        {
            if (ClipManage.CurrentEditFragment.clip is null)
                return;

            if (!Tools.CheckFragmentName(this, ClipManage.CurrentEditFragment.clip!, ClipManage.CurrentEditFragment))
                return;

            if (!Tools.CheckStartAndEndTime())
            {
                MessageBoxEx.Show(this, "结束时间小于开始时间", "时间范围错误", MessageBoxButton.OK, MessageBoxImage.Error);
                return;
            }

            Fragment? fragment = ClipManage.FindFragment(ClipManage.CurrentEditFragment.ID);

            if (fragment is null)
            {
                MessageBoxEx.Show(this, "保存目标不存在，无法完成保存，请尝试使用添加", "保存错误", MessageBoxButton.OK, MessageBoxImage.Error);
                return;
            }

            TimeWindowShow.ShowTimeDialog(this);

            if (fragment.clip != ClipManage.CurrentEditFragment.clip)
            {
                fragment.clip?.Remove(fragment);
                ClipManage.CurrentEditFragment.clip.AddFragment(fragment);
            }
            if (string.IsNullOrEmpty(fragment.InputPath))
                fragment.InputPath = Mec.CurrentMediaPath;
            ClipManage.CurrentEditFragment.CopyTo(fragment);
            fragment.clip?.CallUpdateTimeDuration();
        }

        /// <summary>
        /// 添加片段到一个新的剪辑
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void AddFragmentToNewClip_Click(object sender, RoutedEventArgs e)
        {
            if (!Tools.CheckStartAndEndTime())
            {
                MessageBoxEx.Show(this, "结束时间小于开始时间", "时间范围错误", MessageBoxButton.OK, MessageBoxImage.Error);
                return;
            }

            Fragment fragment = new Fragment();
            ClipManage.CurrentEditFragment!.CopyTo(fragment);
            fragment.ID = ClipManage.NewFragmentID();

            if (!Tools.CheckFragmentName(this, null, ClipManage.CurrentEditFragment))
                return;

            if (!ClipManage.CreateNewClip(out Clip clip))
                return;

            fragment.clip = clip;
            clip.AddFragment(fragment);
            if (string.IsNullOrEmpty(fragment.InputPath))
                fragment.InputPath = Mec.CurrentMediaPath;
            ClipManage.CurrentEditFragment.CopyToSet(fragment);
            clip.Name = fragment.Name;
            ClipManage.DuplicateNameCheck();
        }

        /// <summary>
        /// 启动剪接流程
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private async void SplitAndMergeButton_Click(object sender, RoutedEventArgs e)
        {
            if (ClipManage.TotalFragmentCount < 1)
            {
                MessageBoxEx.Show(this, $"没有片段信息", "操作错误", MessageBoxButton.OK, MessageBoxImage.Error);
                return;
            }
            if (ClipManage.ClipList.Any(p => string.IsNullOrWhiteSpace(p.Name)))
            {
                MessageBoxEx.Show(this, $"剪辑有未命名项，请修正后重试!", "命名错误", MessageBoxButton.OK, MessageBoxImage.Error);
                return;
            }

            if (ClipManage.ClipList.CheckDuplicate(p => p.Name, StringComparer.OrdinalIgnoreCase))
            {
                MessageBoxEx.Show(this, $"剪辑有重名项，请修正后重试!\r\n重名项:\r\n{string.Join("\r\n", ClipManage.ClipList.GroupBy(p => p.Name).Where(p => p.Count() > 1).Select(p => p.Key))}"
                    , "命名错误", MessageBoxButton.OK, MessageBoxImage.Error);
                return;
            }
            StatusInfo.Visibility = Visibility.Collapsed;
            StatusProgress.Visibility = Visibility.Visible;
            TaskFlowProcessor taskProcessors = new TaskFlowProcessor();
            Action<double, double> spc = (max, value) => Dispatcher.BeginInvoke(() => { StatusProgress.Maximum = max; StatusProgress.Value = value; });
            StatusProgress.Visibility = Visibility.Visible;
            taskProcessors.Add(new SplitTaskFlow(spc, this));
            taskProcessors.Add(new MergeTaskFlow(spc, this));

            RootGrid.IsEnabled = false;
            int Result = await taskProcessors.ExecuteAsync(SplitFrameMode.IsChecked!);
            if (Result < 0)
            {
                StatusInfo.Visibility = Visibility.Visible;
                StatusInfo.Background = Brushes.Red;
                StatusInfo.Text = "执行错误";
            }
            else
            {
                StatusInfo.Visibility = Visibility.Visible;
                StatusInfo.Background = Brushes.DarkSeaGreen;
                StatusInfo.Text = "完成";
            }
            RootGrid.IsEnabled = true;
            StatusProgress.Visibility = Visibility.Collapsed;
        }

        public void StatusProgressShow()
        {
            RootGrid.IsEnabled = false;
            StatusInfo.Visibility = Visibility.Collapsed;
            StatusProgress.Visibility = Visibility.Visible;
        }
        public void StatusProgressShowCompleteB(string info)
        {
            RootGrid.IsEnabled = true;
            StatusInfo.Visibility = Visibility.Visible;
            StatusProgress.Visibility = Visibility.Collapsed;
        }
        public void OnlyStatusProgressShow()
        {
            StatusInfo.Visibility = Visibility.Collapsed;
            StatusProgress.Visibility = Visibility.Visible;
        }
        public void StatusProgressShowError(string info)
        {
            StatusInfo.Visibility = Visibility.Visible;
            StatusInfo.Background = Brushes.Red;
            StatusInfo.Text = info;

            RootGrid.IsEnabled = true;
            StatusProgress.Visibility = Visibility.Collapsed;
            ProgressStatistics.Clear();
        }

        public void StatusProgressShowComplete(string info)
        {
            StatusInfo.Visibility = Visibility.Visible;
            StatusInfo.Background = Brushes.DarkSeaGreen;
            StatusInfo.Text = info;

            RootGrid.IsEnabled = true;
            StatusProgress.Visibility = Visibility.Collapsed;
            ProgressStatistics.Clear();
        }


        /// <summary>
        /// 重置开始和结尾时间
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void ResetUITime(object? sender, RoutedEventArgs? e)
        {
            if (sender is null)
            {
                ClipManage.CurrentEditFragment.StartTimeValue = TextBoxTextTimeInputChangedLimit.DefaultHistorical;
                if (Mec.MediaDuration.HasValue)
                    ClipManage.CurrentEditFragment.EndTimeValue = Tools.GetTimeSpanToString(Mec.MediaDuration.Value);
                return;
            }
            if (((Button)sender).Name.EndsWith("Start", StringComparison.OrdinalIgnoreCase))
                ClipManage.CurrentEditFragment.StartTimeValue = TextBoxTextTimeInputChangedLimit.DefaultHistorical;
            else if (Mec.MediaDuration.HasValue)
                ClipManage.CurrentEditFragment.EndTimeValue = Tools.GetTimeSpanToString(Mec.MediaDuration.Value);
        }
        /// <summary>
        /// 监控时间取值范围-禁止结束时间小于开始时间
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void TimeText_TextChanged(object sender, TextChangedEventArgs e)
        {
            TimeSpan start = Tools.GetStringTimeToTimeSpan(ClipManage.CurrentEditFragment.StartTimeValue);
            TimeSpan end = Tools.GetStringTimeToTimeSpan(ClipManage.CurrentEditFragment.EndTimeValue);
            if (start > end)
                ClipManage.CurrentEditFragment.EndTimeValue = Tools.GetTimeSpanToString(start + new TimeSpan(0, 0, 1));
        }

        /// <summary>
        /// 设置影片信息到当前数据
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        public void SetCurrentMediaInfo_Click(object sender, RoutedEventArgs e)
        {
            if (string.IsNullOrEmpty(Mec.CurrentMediaPath))
                return;
            ClipManage.CurrentEditFragment.InputPath = Mec.CurrentMediaPath;
            MediaInfoResult info = GetMediaInfo.GetInfo(ClipManage.CurrentEditFragment.InputPath);
            ClipManage.CurrentEditFragment.Name = Mec.CurrentMediaName;
            ClipManage.CurrentEditFragment.StartTimeValue = TextBoxTextTimeInputChangedLimit.DefaultHistorical;
            ClipManage.CurrentEditFragment.IsVideoStream = info.IsVideoStream;
            ClipManage.CurrentEditFragment.IsAudioStream = info.IsAudioStream;
            ClipManage.CurrentEditFragment.IsSubStream = info.IsSubStream;
            if (info.HasDuration)
                ClipManage.CurrentEditFragment.EndTimeValue = Tools.GetTimeSpanToString(info.Duration);
            if (Mec.MediaDuration.HasValue)
                ClipManage.CurrentEditFragment.EndTimeValue = Tools.GetTimeSpanToString(Mec.MediaDuration.Value);
            ClipManage.CurrentEditFragment.ID = -1;
        }

        /// <summary>
        /// 仅剪辑当前片段
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private async void SplitFragmentButton_Click(object sender, RoutedEventArgs e)
        {
            Fragment fragment = new();
            ClipManage.CurrentEditFragment.CopyTo(fragment);
            string ife = fragment.InputFileExtension;

            Microsoft.Win32.SaveFileDialog sfd = new Microsoft.Win32.SaveFileDialog();
            if (ife.StartsWith(".rm", StringComparison.OrdinalIgnoreCase))
                sfd.Filter = "rm|*.rm|rmvb|*.rmvb|rmv|*.rmv;rma|*.rma";
            else
                sfd.Filter = $"{ife.TrimStart('.')}|*{ife}|mkv|*.mkv|全部|*.*;";
            sfd.Title = "保存媒体文件";
            sfd.CheckPathExists = true;
            sfd.DereferenceLinks = true;
            sfd.OverwritePrompt = true;
            sfd.InitialDirectory = !string.IsNullOrEmpty(MediaElementConcrol.LastPath) && Directory.Exists(MediaElementConcrol.LastPath) ? MediaElementConcrol.LastPath : Path.GetDirectoryName(fragment.InputPath);
            sfd.AddExtension = true;
            sfd.DefaultExt = fragment.InputFileExtension;
            sfd.ValidateNames = true;
            bool? result = sfd.ShowDialog(Owner);
            if (!result.HasValue || !result.Value)
                return;
            fragment.TempWritePath = Path.Combine(sfd.FileName);
            LosslessSplitTask task = new LosslessSplitTask(fragment, SplitFrameMode.IsChecked.HasValue ? SplitFrameMode.IsChecked.Value : true);
            RootGrid.IsEnabled = false;

            StatusProgress.Visibility = Visibility.Visible;
            StatusInfo.Visibility = Visibility.Collapsed;
            bool IsRun = true;

            _ = Task.Run(() =>
              {
                  while (IsRun)
                  {
                      Dispatcher.BeginInvoke(() => { StatusProgress.Maximum = fragment.FragmentDuration.TotalSeconds; StatusProgress.Value = task.ProcessorTime.TotalSeconds; });
                      Thread.Sleep(500);
                  }
              });

            int ResultInt = await Task.Run(() =>
            {
                task.Execute();
                return -1;
            });

            if (ResultInt > 0)
                return;
            IsRun = false;

            RootGrid.IsEnabled = false;
            if (task.IsError)
            {
                StatusInfo.Visibility = Visibility.Visible;
                StatusInfo.Background = Brushes.Red;
                StatusInfo.Text = "操作失败";
            }
            StatusInfo.Visibility = Visibility.Visible;
            StatusInfo.Background = Brushes.DarkSeaGreen;
            StatusInfo.Text = "完成";
            RootGrid.IsEnabled = true;
            StatusProgress.Visibility = Visibility.Collapsed;

        }

        private void SubPage_LoadCompleted(object sender, System.Windows.Navigation.NavigationEventArgs e)
        {
            Frame frame = (Frame)sender;
            if (frame.Content is ConfigSetting page)
                page.ParentWindow = this;

        }

        private void Setting_Button_Click(object sender, RoutedEventArgs e)
        {
            SettingWindow.SettingShow();
        }

        private void ConfigSetting_Button_Click(object sender, RoutedEventArgs e)
        {
            ProcessorSettings.ShowSettingsDialog();
        }

        private void OpenLog_Button_Click(object sender, RoutedEventArgs e)
        {
            LogInfoWindow.ShowLogInfo();
        }

        private void OpenClipList_Button_Click(object sender, RoutedEventArgs e)
        {
            ClipManage.LoadClipList();
        }

        private void SaveClipList_Button_Click(object sender, RoutedEventArgs e)
        {
            ClipManage.SaveClipList();
        }

        private void RemoveAllClips_Button_Click(object sender, RoutedEventArgs e)
        {
            ClipManage.ClipList.Clear();
            ClipsGroupManage.Clear();
        }

        private void FragmentRightButtonContextMenuItem_Click(object sender, RoutedEventArgs e)
        {
            if (sender is not MenuItem menuItem)
                return;
            if (menuItem.Parent is not ContextMenu cm)
                return;
            if (ContextMenuService.GetPlacementTarget(cm) is not FrameworkElement fe || fe.DataContext is not Fragment SelectFragment)
                return;

            switch (menuItem.Tag)
            {
                case "SplitFragment":
                    Tools.SplitFragment(SelectFragment, err =>
                    {
                        StatusInfo.Visibility = Visibility.Visible;
                        StatusInfo.Background = Brushes.Red;
                        StatusInfo.Text = err;
                    });
                    return;
                case "OpenToSource":
                    Tools.Explorer(SelectFragment.InputPath, true);
                    return;
                case "OpenToOutput":
                    Tools.Explorer(SelectFragment.clip!.OutDirectory);
                    return;
                case "Deltet":
                    SelectFragment.clip?.Remove(SelectFragment);
                    return;
                case "DeltetAll":
                    SelectFragment.clip?.Clear();
                    return;
                case "MoveTo":
                    Tools.FileMoveTo(this, SelectFragment);
                    return;
                case "AllMoveTo":
                    Tools.FileMoveTo(this, ClipManage.ClipList.SelectMany(p => p.Fragments).ToArray());
                    return;
                case "DeleteAllSelect":
                    if (MessageBoxEx.Show(this, "该操作将删除多个项，且不可恢复，确认删除么？", "删除确认", MessageBoxButton.OKCancel, MessageBoxImage.Warning, MessageBoxResult.Cancel) == MessageBoxResult.OK)
                    {
                        Clip _clip = SelectFragment.clip!;
                        SelectFragment.clip!.Fragments.Where(p => p.IsSelected).ToArray().ForEach(p => _clip.Remove(p));
                    }
                    return;
                case "SelectAll":
                    SelectFragment.clip!.Fragments.ForEach(p => p.IsSelected = true);
                    return;
                case "DeselectAll":
                    SelectFragment.clip!.Fragments.ForEach(p => p.IsSelected = false);
                    return;
                case "SelectInvert":
                    SelectFragment.clip!.Fragments.ForEach(p => p.IsSelected = !p.IsSelected);
                    return;
                default:
                    return;
            }
        }

        private void OpenCacheDirectory_Button_Click(object sender, RoutedEventArgs e)
        {
            Tools.Explorer(Config.CacheDirectory);
        }

        private void DeleteAllFileInCacheDirectory_Button_Click(object sender, RoutedEventArgs e)
        {
            TimeWindowShow.ShowTimeDialog(this, "清理中...");
            Directory.GetFileSystemEntries(Config.CacheDirectory, "*", SearchOption.TopDirectoryOnly)
                .ForEach(p =>
                {
                    if (Directory.Exists(p))
                        Directory.Delete(p, true);
                    else if (File.Exists(p))
                        File.Delete(p);
                });
        }

        private void CloseSub_Storyboard_Completed(object sender, EventArgs e)
        {
            SubPage.Visibility = Visibility.Collapsed;

            SubPage.Source = null;

        }

        private void SubPage_Navigated(object sender, System.Windows.Navigation.NavigationEventArgs e)
        {
            SubPage.RemoveBackEntry();
        }

        private void ClipListBox_PreviewMouseWheel(object sender, MouseWheelEventArgs e)
        {
            if (!e.Handled)
            {
                e.Handled = true;
                //if (((Control)sender).Parent is UIElement parent)
                //    parent.RaiseEvent(new MouseWheelEventArgs(e.MouseDevice, e.Timestamp, e.Delta) { RoutedEvent = MouseWheelEvent, Source = sender });
                ClipListViewer.RaiseEvent(new MouseWheelEventArgs(e.MouseDevice, e.Timestamp, e.Delta) { RoutedEvent = MouseWheelEvent, Source = sender });
            }

        }

        private void window_Closed(object sender, EventArgs e)
        {
            ProcessShell.pss.ForEach(pss => pss.Kill());
        }

        private void CancelRun_Click(object sender, RoutedEventArgs e)
        {
            if (Tools.CurrentCancelToken is null)
            {
                if (ProcessShell.pss.Count > 0)
                    ProcessShell.pss.ForEach((pss) => pss.Kill());
                return;
            }
            Tools.CurrentCancelToken.Cancel();
            Task.Run(() =>
            {
                Thread.Sleep(300);
                ProcessShell.pss.ForEach((pss) => pss.Kill());
                //沉余措施
                Thread.Sleep(1000);
                while (Tools.CurrentCancelToken is not null)
                {
                    if (ProcessShell.pss.Count > 0)
                        ProcessShell.pss.ForEach((pss) => pss.Kill());
                    Thread.Sleep(100);
                }
                Thread.Sleep(500);
                if (ProcessShell.pss.Count > 0)
                    ProcessShell.pss.ForEach((pss) => pss.Kill());
            });
        }

        private void ReOpen_Button_Click(object sender, RoutedEventArgs e) => Mec.ReOpen();

        private void Slider_MouseDown_GetFocus(object sender, MouseButtonEventArgs e) => ((FrameworkElement)sender).Focus();

        private void StatusBar_TimeRemainingItem_IsVisibleChanged(object sender, DependencyPropertyChangedEventArgs e)
        { if ((bool)e.NewValue) ProgressRatioToTimeRemainingConvert.Init(); }

        private void TimeLineCheck_Checked(object sender, RoutedEventArgs e)
        {
            if (TimeLineCheck.IsChecked!.Value)
            {
                TimeSlidersRoot.Visibility = Visibility.Hidden;
                TimeLine.IsEnabled = true;
                TimeLineRoot.Visibility = Visibility.Visible;
                TimeLine.OnShow(JdSlider.Value);
            }
            else
            {
                TimeSlidersRoot.Visibility = Visibility.Visible;
                TimeLine.IsEnabled = false;
                TimeLineRoot.Visibility = Visibility.Hidden;
            }
        }

        /// <summary>
        /// 打开进度详情窗口
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void ProgressDetails_Click(object sender, RoutedEventArgs e)
        {
            if (ProgressDetailsWindow.Current == null)
            {
                ProgressDetailsWindow.Current = new ProgressDetailsWindow();
                ProgressDetailsWindow.Current.Show();
            }
            else ProgressDetailsWindow.Current.Activate();
        }

        //分割剪辑
        private void CutFragment_Click(object sender, RoutedEventArgs e)
        {
            SplitFragmentWindow window = new SplitFragmentWindow(SplitFragmentWindow.SplitModeEnum.UserTime, ClipManage.CurrentEditFragment);
            if (window.ShowDialog() is false)
                return;
            Tools.SpecifyTimeSplitCut(window.resultData);
        }

        private void GroupButton_Click(object sender, RoutedEventArgs e)
        {
            Button button = (Button)sender;
            Clip clip = (Clip)button.DataContext;
            if (clip.Group is not null)
            {
                if (MessageBoxEx.Show(this, "确认删除这个组么?", "删除组确认", MessageBoxButton.OKCancel, MessageBoxImage.Warning, MessageBoxResult.Cancel) == MessageBoxResult.Cancel)
                    return;
                ClipsGroupManage.GroupDelet(clip);
                button.Content = "创建组";
            }
            else
            {
                ClipsGroupManage.GroupCreate(clip);

                button.Content = "删除组";
            }
            clip.SendNotifyPropertyChanged(nameof(clip.IsExistsInGroup));
        }

        private void GroupList_PreviewDrop(object sender, DragEventArgs e)
        {
            DragDropData? dragDropData = e.Data.GetData(typeof(DragDropData)) as DragDropData;
            if (dragDropData is null || dragDropData.DataType != typeof(Clip))
                return;
            Tools.ObservableCollection_Drop<ListBoxItem, Clip>(sender, e, (ObservableCollection<Clip>)((ListBox)sender).ItemsSource, DragDropEffects.Copy, true, true, DisableBoundaryLimitation: true);
            e.Handled = true;
            return;
        }

        private void ClipUIHead_PreviewMouseMove(object sender, MouseEventArgs e)
        {
            if (e.LeftButton != MouseButtonState.Pressed)
                return;
            MainWindow.CloseStatusInfo();
            Tools.ObservableCollection_DropMove<ListBox, Clip>(sender, e, DragDropEffects.Copy);
        }

        private void GroupRightButtonContextMenu_MenuItem_Click(object sender, RoutedEventArgs e)
        {
            if (sender is not MenuItem menuItem)
                return;
            if (menuItem.Parent is not ContextMenu cm)
                return;
            if (ContextMenuService.GetPlacementTarget(cm) is not FrameworkElement fe)
                return;
            if (Tools.GetParent<ListBox>(fe, out ListBox? listBox))
            {
                ObservableCollection<Clip> clips = (ObservableCollection<Clip>)listBox!.ItemsSource;
                foreach (Clip item in listBox!.SelectedItems.Cast<Clip>().ToArray())
                    clips.Remove(item);
            }
        }

        private void VideoDetect_Click(object sender, RoutedEventArgs e)
        {
            VideoDetectWindow window = new VideoDetectWindow();
            window.FilePath = Mec.CurrentMediaPath;
            if ((!window.ShowDialog() ?? true) || window.Result == null || window.Result.Length < 1)
                return;
            Tools.GetChilds<TimeLineTickMarks>(TimeLine).First().Set(window.Result);
            if (!window.IsEnableAutoCut)
                return;
            ClipManage.CutSplitFile(window.FilePath, window.Result, IsEnableCutToSingleClips: window.IsEnableCutToNewClip);
        }

        private void CopyTimesToClipboard(Clip clip)
        {
            if (clip.Count < 1)
                return;
            TimeSpan[] Durations = clip.Fragments.Select(p => p.FragmentDuration).ToArray();
            TimeSpan[] IncrementTimes = new TimeSpan[Durations.Length];
            IncrementTimes[0] = Durations[0];

            for (int i = 1; i < Durations.Length; i++)
                IncrementTimes[i] = IncrementTimes[i - 1] + Durations[i];
            StringBuilder sb = new();
            MessageBoxResult SelectResult = MessageBoxEx.CustomButtonTextMessageBoxShow(this, "格式选择", "要拷贝那种格式?", MessageBoxButton.YesNoCancel, MessageBoxImage.Question, MessageBoxResult.Yes, null, "时间格式", "秒格式", "分格式");

            switch (SelectResult)
            {
                case MessageBoxResult.No:
                    foreach (TimeSpan ts in IncrementTimes)
                        sb.AppendLine(ts.TotalSeconds.ToString());
                    break;
                case MessageBoxResult.Cancel:
                    foreach (TimeSpan ts in IncrementTimes)
                        sb.AppendLine(ts.TotalMinutes.ToString());
                    break;
                default:
                    foreach (TimeSpan ts in IncrementTimes)
                        sb.AppendLine(Tools.GetTimeSpanToString_HoursMinutesSecondsDigit(ts));
                    break;
            }
            System.Windows.Forms.Clipboard.SetText(sb.ToString(), System.Windows.Forms.TextDataFormat.Text);
            //Clipboard.SetText(sb.ToString());
        }

        private void CopyTimesToClipboard_Seconds(Clip clip)
        {
            if (clip.Count < 1)
                return;
            TimeSpan[] Durations = clip.Fragments.Select(p => p.FragmentDuration).ToArray();
            TimeSpan[] IncrementTimes = new TimeSpan[Durations.Length];
            IncrementTimes[0] = Durations[0];

            for (int i = 1; i < Durations.Length; i++)
                IncrementTimes[i] = IncrementTimes[i - 1] + Durations[i];
            StringBuilder sb = new();
            MessageBoxResult SelectResult = MessageBoxEx.CustomButtonTextMessageBoxShow(this, "格式选择", "要拷贝那种格式?", MessageBoxButton.YesNoCancel, MessageBoxImage.Question, MessageBoxResult.Yes, null, "向上舍入", "四舍五入", "向下舍入");

            switch (SelectResult)
            {
                case MessageBoxResult.No:
                    foreach (TimeSpan ts in IncrementTimes)
                        sb.AppendLine(Math.Round(ts.TotalSeconds, MidpointRounding.AwayFromZero).ToString());
                    break;
                case MessageBoxResult.Cancel:
                    foreach (TimeSpan ts in IncrementTimes)
                        sb.AppendLine(Math.Floor(ts.TotalSeconds).ToString());
                    break;
                default:
                    foreach (TimeSpan ts in IncrementTimes)
                        sb.AppendLine(Math.Ceiling(ts.TotalSeconds).ToString());
                    break;
            }
            System.Windows.Forms.Clipboard.SetText(sb.ToString(), System.Windows.Forms.TextDataFormat.Text);
            //Clipboard.SetText(sb.ToString());
        }

        private void CopyTimesToClipboard_Minutes(Clip clip)
        {
            if (clip.Count < 1)
                return;
            TimeSpan[] Durations = clip.Fragments.Select(p => p.FragmentDuration).ToArray();
            TimeSpan[] IncrementTimes = new TimeSpan[Durations.Length];
            IncrementTimes[0] = Durations[0];

            for (int i = 1; i < Durations.Length; i++)
                IncrementTimes[i] = IncrementTimes[i - 1] + Durations[i];
            StringBuilder sb = new();
            MessageBoxResult SelectResult = MessageBoxEx.CustomButtonTextMessageBoxShow(this, "格式选择", "要拷贝那种格式?", MessageBoxButton.YesNoCancel, MessageBoxImage.Question, MessageBoxResult.Yes, null, "向上舍入", "四舍五入", "向下舍入");

            switch (SelectResult)
            {
                case MessageBoxResult.No:
                    foreach (TimeSpan ts in IncrementTimes)
                        sb.AppendLine(Math.Round(ts.TotalMinutes, MidpointRounding.AwayFromZero).ToString());
                    break;
                case MessageBoxResult.Cancel:
                    foreach (TimeSpan ts in IncrementTimes)
                        sb.AppendLine(Math.Floor(ts.TotalMinutes).ToString());
                    break;
                default:
                    foreach (TimeSpan ts in IncrementTimes)
                        sb.AppendLine(Math.Ceiling(ts.TotalMinutes).ToString());
                    break;
            }
            System.Windows.Forms.Clipboard.SetText(sb.ToString(), System.Windows.Forms.TextDataFormat.Text);
            //Clipboard.SetText(sb.ToString());
        }

        private async void ClipHeaderRightClickMenuItem_Click(object sender, RoutedEventArgs e)
        {
            //ClipManage.ClipList.Where(p => p.IsUIFreeze).ForEach(p => p.IsSelected = false);
            if (sender is not MenuItem menuItem)
                return;
            if (menuItem.Parent is not ContextMenu cm)
                return;
            if (ContextMenuService.GetPlacementTarget(cm) is not FrameworkElement fe || fe.DataContext is not Clip SelectClip)
                return;

            switch (menuItem.Tag)
            {
                case "RemoveAllSelect":
                    if (MessageBoxEx.Show(this, "该操作将移除多个项，且不可恢复，确认移除么？", "移除确认", MessageBoxButton.OKCancel, MessageBoxImage.Warning, MessageBoxResult.Cancel) == MessageBoxResult.OK)
                    {
                        Clip[] clips = ClipManage.ClipList.Where(p => p.IsSelected && !p.IsUIFreeze).ToArray();
                        clips.ForEach(p => ClipsGroupManage.GroupDelet(p));
                        clips.ForEach(p => ClipManage.ClipList.Remove(p));
                    }
                    return;
                case "DeleteAllSelect":
                    if (MessageBoxEx.Show(this, "该操作将删除多个项，且不可恢复，确认删除么？", "删除确认", MessageBoxButton.OKCancel, MessageBoxImage.Warning, MessageBoxResult.Cancel) == MessageBoxResult.OK)
                    {
                        Clip[] clips = ClipManage.ClipList.Where(p => p.IsSelected && !p.IsUIFreeze).ToArray();
                        ClipsGroupManage.FormAssociatedGroupRemove(clips);
                        clips.SelectMany(p => p.Fragments.Select(f => f.InputPath)).Distinct(StringComparer.OrdinalIgnoreCase).ToArray().ForEach(p =>
                            Microsoft.VisualBasic.FileIO.FileSystem.DeleteFile(p, Microsoft.VisualBasic.FileIO.UIOption.AllDialogs, Microsoft.VisualBasic.FileIO.RecycleOption.SendToRecycleBin));
                        if (MessageBoxEx.Show(this, "原始文件以删除,是否同时删除剪辑？", "删除确认", MessageBoxButton.YesNo, MessageBoxImage.Question, MessageBoxResult.No) == MessageBoxResult.Yes)
                            clips.ForEach(p => ClipManage.ClipList.Remove(p));
                    }
                    return;
                case "SelectAll":
                    ClipManage.ClipList.Where(p => !p.IsUIFreeze).ForEach(p => p.IsSelected = true);
                    return;
                case "CancelAll":
                    ClipManage.ClipList.ForEach(p => p.IsSelected = false);
                    return;
                case "SelectInvert":
                    ClipManage.ClipList.Where(p => !p.IsUIFreeze).ForEach(p => p.IsSelected = !p.IsSelected);
                    return;
                case "ExecuteOut":
                    await ClipTaskProcessor.ExecuteOut(this, [SelectClip], SplitFrameMode.IsChecked ?? false, StatusProgress);
                    return;
                case "Merge":
                    await ClipTaskProcessor.SingleClipMergeAsync(this, SelectClip, StatusProgress);
                    return;
                case "TSMerge":
                    await ClipTaskProcessor.SingleClipTSMergeAsync(this, SelectClip, StatusProgress);
                    return;
                case "OutputValidation":
                    ClipManage.ClipList.Where(p => p.IsSelected).ForEach(p => p.CompletionStatusValidation());
                    return;
                case "CopyTime":
                    CopyTimesToClipboard(SelectClip);
                    return;
                case "CopyTime_Seconds":
                    CopyTimesToClipboard_Seconds(SelectClip);
                    return;
                case "CopyTime_Minutes":
                    CopyTimesToClipboard_Minutes(SelectClip);
                    return;
                default: return;
            }
        }



        private void MediaPlayer_PreviewMouseDown(object sender, MouseButtonEventArgs e)
        {
            if (TimeLineCheck.IsChecked ?? false)
                TimeLine.Focus();
            else
                JdSlider.Focus();
        }

        private void PlayerSpeed_Slider_PreviewMouseDoubleClick(object sender, MouseButtonEventArgs e) => ((Slider)sender).Value = 1;

        private void WavSpectrogramColor_Button_Click(object sender, RoutedEventArgs e)
        {
            Button button = (Button)sender;
            TimeLine.WavSpectrogramColor = AudioWavSpectrogram.WavSpectrogramColors[int.Parse(button.Tag.ToString()!)];
        }


        private record struct AddToGruopEventData(Clip clip, ClipsGroup group);
        private void ClipHeaderRightClickMenu_ContextMenu_ContextMenuOpening(object sender, ContextMenuEventArgs e)
        {
            if (ClipsGroupManage.GroupsCount < 1)
                return;
            foreach (ClipsGroup group in ClipsGroupManage.AllGroup)
            {
                MenuItem item = new MenuItem();
                item.Header = $"{group.MainClip.IndexUI}  {group.MainClip.Name}";
                item.Tag = new AddToGruopEventData((Clip)((FrameworkElement)sender).DataContext, group);
                item.Click += AddToSelectGruop_Click;
                AddToGruop.Items.Add(item);
            }
            ContextMenu cm = ((FrameworkElement)sender).ContextMenu;
            cm.Items.Add(AddToGruop);
        }

        private void AddToSelectGruop_Click(object sender, RoutedEventArgs e)
        {
            AddToGruopEventData eventData = (AddToGruopEventData)((MenuItem)sender).Tag;
            if (!eventData.clip.IsSelected)
                eventData.group.Add(eventData.clip);
            else ClipManage.ClipList.Where(p => p.IsSelected).ForEach(eventData.group.Add);
        }

        private void ClipHeaderRightClickMenu_ContextMenu_ContextMenuClosing(object sender, ContextMenuEventArgs e)
        {
            ContextMenu cm = ((FrameworkElement)sender).ContextMenu;
            cm.Items.Remove(AddToGruop);
            AddToGruop.Items.Clear();
        }

        private void PreviousFrame_Checked(object sender, RoutedEventArgs e)
        {
            if (PreviousFrame == null || NextFrame == null)
                return;
            if (PreviousFrame.IsButtonChecked && NextFrame.IsButtonChecked)
                NextFrame.IsButtonChecked = false;
        }

        private void NextFrame_Checked(object sender, RoutedEventArgs e)
        {
            if (PreviousFrame == null || NextFrame == null)
                return;
            if (PreviousFrame.IsButtonChecked && NextFrame.IsButtonChecked)
                PreviousFrame.IsButtonChecked = false;
        }

        
        public async Task ToFrameTimeAsync() => await ToTime(false);
        public async Task ToKeyFrameTimeAsync() => await ToTime(true);
        private void ToFrameTime_Click(object sender, RoutedEventArgs e) { TimeWindowShow.ToFrameTimeDialog(this); }
        private void ToKeyframe_Click(object sender, RoutedEventArgs e) { TimeWindowShow.ToKeyFrameTimeDialog(this); }
        private async Task ToTime(bool IsKeyFrame = false)
        {
            TimeSpan currentTime = Mec.CurrentTimePosition;
            string Path = Mec.CurrentMediaPath;

            FrameTimeInfo[] FrameTime = await Task.Run(() => GetFrameData.GetFramesTimes(Path, [new(currentTime, IsKeyFrame)]));

            if (FrameTime == null || FrameTime.Length < 1)
                return;
            FrameTimeInfo info = FrameTime[0];
            if (PreviousFrame.IsButtonChecked)
                Mec.CurrentTimePosition = info.Start;
            else
                Mec.CurrentTimePosition = info.NextStart;
        }
    
    }
    
}
